/*
 *  fgfiltering.cpp
 *  opencvTest
 *
 *  Created by a1gucis on 3/6/09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */
#include "fgfiltering.h"
// Approx.threshold - the bigger it is, the simpler is the boundary 
// 
#define CVCONTOUR_APPROX_LEVEL  2 
// How many iterations of erosion and/or dilation there should be 
// 
#define CVCLOSE_ITR  1 

const CvScalar CVX_WHITE = CV_RGB(0xff,0xff,0xff); 
const CvScalar CVX_BLACK = CV_RGB(0x00,0x00,0x00);
// This cleans up the foreground segmentation mask derived from calls 
// to backgroundDiff 
// 
// mask          Is a grayscale (8-bit depth) “raw” mask image that 
//               will be cleaned up 
// 
// OPTIONAL PARAMETERS: 
// poly1_hull0   If set, approximate connected component by 
//                 (DEFAULT) polygon, or else convex hull (0) 
// perimScale    Len = image (width+height)/perimScale. If contour 
//                 len < this, delete that contour (DEFAULT: 4) 
// num           Maximum number of rectangles and/or centers to 
//                 return; on return, will contain number filled 
//                 (DEFAULT: NULL) 
// bbs           Pointer to bounding box rectangle vector of 
//                 length num. (DEFAULT SETTING: NULL) 
// centers      Pointer to contour centers vector of length 
//                 num (DEFAULT: NULL) 
// 
FgFiltering::FgFiltering(int height, int width) {
	chan1 = cvCreateImage(cvSize(height, width), IPL_DEPTH_8U, 1);
	chan2 = cvCreateImage(cvSize(height, width), IPL_DEPTH_8U, 1);
	chan3 = cvCreateImage(cvSize(height, width), IPL_DEPTH_8U, 1);
	temp = cvCreateImage(cvSize(height, width), IPL_DEPTH_8U, 1);
}

FgFiltering::~FgFiltering() {
	cvReleaseImage(&chan1);
	cvReleaseImage(&chan2);
	cvReleaseImage(&chan3);
	cvReleaseImage(&temp);
}

void FgFiltering::findConnectedComponents(IplImage* mask, int poly1_hull0, float perimScale, int* num, CvRect* bbs, CvPoint*  centers) {
	static CvMemStorage*   mem_storage = NULL; 
	static CvSeq*          contours    = NULL; 
	//CLEAN UP RAW MASK 
	// 
	cvMorphologyEx( mask, mask, 0, 0, CV_MOP_OPEN,  CVCLOSE_ITR ); 
	cvMorphologyEx( mask, mask, 0, 0, CV_MOP_CLOSE, CVCLOSE_ITR ); 
	//FIND CONTOURS AROUND ONLY BIGGER REGIONS 
	// 
	if( mem_storage==NULL ) { 
		mem_storage = cvCreateMemStorage(0); 
	} else { 
		cvClearMemStorage(mem_storage); 
	} 
	CvContourScanner scanner = cvStartFindContours(mask, mem_storage, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); 
	CvSeq* c; 
	int numCont = 0; 
	while( (c = cvFindNextContour( scanner )) != NULL ) { 
		double len = cvContourPerimeter( c ); 
		// calculate perimeter len threshold: 
		// 
		double q = (mask->height + mask->width)/perimScale; 
		//Get rid of blob if its perimeter is too small:
		// 
		if( len < q ) { 
			cvSubstituteContour( scanner, NULL ); 
		} else { 
			// Smooth its edges if its large enough 
			// 
			CvSeq* c_new; 
			if( poly1_hull0 ) { 
				// Polygonal approximation 
				// 
				c_new = cvApproxPoly(c, sizeof(CvContour), mem_storage, CV_POLY_APPROX_DP, CVCONTOUR_APPROX_LEVEL, 0); 
			} else { 
				// Convex Hull of the segmentation 
				// 
				c_new = cvConvexHull2(c, mem_storage,CV_CLOCKWISE, 1); 
			} 
			cvSubstituteContour( scanner, c_new ); 
			numCont++; 
		} 
	}
	contours = cvEndFindContours( &scanner ); 
	
	// PAINT THE FOUND REGIONS BACK INTO THE IMAGE 
	// 
	cvZero( mask ); 
	IplImage *maskTemp;
	// CALC CENTER OF MASS AND/OR BOUNDING RECTANGLES 
	// 
	if(num != NULL) { 
		//User wants to collect statistics 
		// 
		int N = *num, numFilled = 0, i=0; 
		CvMoments moments; 
		double M00, M01, M10; 
		maskTemp = cvCloneImage(mask); 
		for(i=0, c=contours; c != NULL; c = c->h_next,i++ ) { 
			if(i < N) { 
				// Only process up to *num of them 
				// 
				cvDrawContours(maskTemp, c, CVX_WHITE, CVX_WHITE, -1, CV_FILLED, 8); 
				// Find the center of each contour 
				// 
				if(centers != NULL) { 
					cvMoments(maskTemp,&moments,1); 
					M00 = cvGetSpatialMoment(&moments,0,0); 
					M10 = cvGetSpatialMoment(&moments,1,0); 
					M01 = cvGetSpatialMoment(&moments,0,1); 
					centers[i].x = (int)(M10/M00); 
					centers[i].y = (int)(M01/M00); 
				} 
				//Bounding rectangles around blobs 
				// 
				if(bbs != NULL) { 
					bbs[i] = cvBoundingRect(c); 
				} 
				cvZero(maskTemp); 
				numFilled++; 
			}
			// Draw filled contours into mask 
			// 
			cvDrawContours(mask, c, CVX_WHITE, CVX_WHITE, -1, CV_FILLED, 8); 
		}                               //end looping over contours 
		*num = numFilled; 
		cvReleaseImage( &maskTemp); 
	}
	// ELSE JUST DRAW PROCESSED CONTOURS INTO THE MASK 
	// 
	else { 
		// The user doesn’t want statistics, just draw the contours 
		// 
		for( c=contours; c != NULL; c = c->h_next ) { 
			cvDrawContours(mask, c, CVX_WHITE, CVX_BLACK, -1, CV_FILLED, 8); 
		} 
	} 
}

void FgFiltering::segregateTrueForeground(IplImage *mask, IplImage *raw, IplImage *fg) {
	cvSplit(raw, chan1, chan2, chan3, 0);
	cvAnd(chan1, mask, chan1);
	cvAnd(chan2, mask, chan2);
	cvAnd(chan3, mask, chan3);
	
	cvMerge(chan1, chan2, chan3, NULL, fg);	
}
